#ifndef B2D_ISHADERPROGRAM_H
#define B2D_ISHADERPROGRAM_H

#include "IGraphicsResource.h"
#include "BaseResource.h"

NS_B2D_BEGIN

class ITexture;

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Shader type.
////////////////////////////////////////////////////////////////////////////////////////////////////
enum class B2D_API ShaderType
{
	VERTEX,
	FRAGMENT,
	//GEOMETRY	= GL_GEOMETRY_SHADER
};

////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Interface for shaders.
////////////////////////////////////////////////////////////////////////////////////////////////////
class B2D_API IShader
	: public IUncopyable
	, public IGraphicsResource
{
	B2D_INTERFACE(IShader)

public:
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Gets the API specific handle.
	///
	/// \return	The handle.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void* getHandle(void) const = 0;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Query if this object is initialised.
	///
	/// \return	true if initialised, false if not.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual bool isInitialised(void) const = 0;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Get type of shader.
	///
	/// \return	The type.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual ShaderType getType(void) const = 0;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Query if this shader is compiled.
	///
	/// \return	true if compiled, false if not.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual bool isCompiled(void) const = 0;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Set source and compile the shader.
	/// 		Note that this requires the string to be null-terminated.
	///
	/// \param	src	Source for the shader.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void compileWithSource(const char* src) = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Set source of given size and compile shader.
	///
	/// \param	src   	Source for the shader.
	/// \param	length	The length in bytes.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void compileWithSource(const char* src, size_t length) = 0;
};


////////////////////////////////////////////////////////////////////////////////////////////////////
/// \brief	Interface for shader programs.
////////////////////////////////////////////////////////////////////////////////////////////////////
class B2D_API IShaderProgram
	: public BaseResource
	, public IGraphicsResource
{
	B2D_RESOURCE
	B2D_INTERFACE(IShaderProgram)

public:
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Array indexer operator.
	/// 		Gets shader by type.
	///
	/// \param	type	The type.
	///
	/// \return	The shader of requested time.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual IShader* operator[] (ShaderType type) const = 0;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Query if this shader program is initialised.
	///
	/// \return	true if initialised, false if not.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual bool isInitialised(void) const = 0;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Query if this shader program has been linked.
	///
	/// \return	true if linked, false if not.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual bool isLinked(void) const = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Check if relinked is required.
	///
	/// \return	true if needs relinking, false otherwise.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual bool needsRelinking(void) const = 0;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Attach a shader of given type to this program.
	///
	/// \param	type	The type of shader.
	///
	/// \return	Shader of requested type.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual IShader* addShader(ShaderType type) = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Remove a shader of given type.
	///
	/// \param	type	The type.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void removeShader(ShaderType type) = 0;
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Get the shader of the given type attached to this program (if available)
	///
	/// \param	type	The type.
	///
	/// \return	null if not available, the shader otherwise.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual IShader* getShader(ShaderType type) const = 0;
	
	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	(Re)Link the program with the attached shaders.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual void link(void) = 0;


	////////////////////////////////////////////////////////////////////////////////////////////////////
	/// \brief	Query if a uniform with given name exists in this shader program.
	///
	/// \param	name	The name of the uniform.
	///
	/// \return	true if exists, false if not.
	////////////////////////////////////////////////////////////////////////////////////////////////////
	virtual bool hasUniform(const string& name) const = 0;

	virtual void setUniform1i(const string& name, int value) = 0;
	virtual void setUniform1f(const string& name, float value) = 0;
	virtual void setUniform2i(const string& name, int value0, int value1) = 0;
	virtual void setUniform2f(const string& name, float value0, float value1) = 0;
	virtual void setUniform3i(const string& name, int value0, int value1, int value2) = 0;
	virtual void setUniform3f(const string& name, float value0, float value1, float value2) = 0;
	virtual void setUniform4i(const string& name, int value0, int value1, int value2, int value3) = 0;
	virtual void setUniform4f(const string& name, float value0, float value1, float value2, float value3) = 0;
	virtual void setUniform1iv(const string& name, size_t count, const int* values) = 0;
	virtual void setUniform1fv(const string& name, size_t count, const float* values) = 0;
	virtual void setUniform2iv(const string& name, size_t count, const int* values) = 0;
	virtual void setUniform2fv(const string& name, size_t count, const float* values) = 0;
	virtual void setUniform3iv(const string& name, size_t count, const int* values) = 0;
	virtual void setUniform3fv(const string& name, size_t count, const float* values) = 0;
	virtual void setUniform4iv(const string& name, size_t count, const int* values) = 0;
	virtual void setUniform4fv(const string& name, size_t count, const float* values) = 0;
	virtual void setUniformMat2fv(const string& name, size_t count, bool transpose, const float* values) = 0;
	virtual void setUniformMat3fv(const string& name, size_t count, bool transpose, const float* values) = 0;
	virtual void setUniformMat4fv(const string& name, size_t count, bool transpose, const float* values) = 0;
	virtual void setUniformSampler(const string& name, const ITexture& sampler) = 0;
};


typedef std::shared_ptr<IShaderProgram> ShaderProgramPtr;

NS_B2D_END

#endif